"""
爬虫思路
（一）数据来源分析
    1、爬什么
        视频、标题

    2、去哪爬 ==> url
        2.2 分析资源是静态加载的还是动态加载的
            网站源代码里面静态加载了M3U8

            客户端通过请求M3U8文件获取视频分段的地址，然后根据分段地址逐个下载并播放视频。
            视频一般都是动态加载的，但也不一定。实际去检查一下就知道了
            一小段ts的url地址： https://vip.ffzy-online5.com/20230406/10242_69c3285e/2000k/hls/4bc57e99018000291.ts
                               https://vip.ffzy-online5.com/20230406/10242_69c3285e/2000k/hls/mixed.m3u8

（二）爬虫代码实现
    1、发送请求
    2、获取数据
    3、解析数据
        发送请求
    4、保存数据
"""
import requests
import re
import os
import json
import time


# 发送请求获取视频ts
def get_ts(result, headers, mixed_m3u8_url):
    try:
        # 解析mixed.m3u8构造ts_url
        with open(f'./白狐视频/{result["vod_name"]}/mixed.m3u8', 'r') as f:
            ts_list = f.readlines()
            for ts in ts_list:
                if ts.startswith('#EX'):  # 判断哪些是以#开头
                    ts_list.remove(ts)  # 将其丢弃
        for ts in ts_list:
            try:
                ts = ts.strip()  # 清除字符串两头的空白字符之后要重新复制给ts，千万别忘了
                ts_url = os.path.dirname(mixed_m3u8_url) + '/' + ts
                print("正在爬取 =", ts_url)
                resp = requests.get(url=ts_url.strip(), headers=headers)
                resp.raise_for_status()
                print('爬取视频成功！')
                save_ts(result['vod_name'], resp.content)
                time.sleep(2)
            except:
                pass
    except BaseException as e:
        print('爬取视频失败！', e)
        return None


# 保存ts视频片段
def save_ts(filename, ts_video):
    with open(f'./白狐视频/{filename}/video.mp4', 'ab') as f:
        """
        a ==> add 追加写入
        w ==> write 重新写入
        b ==> binary 二进制
        """
        f.write(ts_video)
        print('视频保存成功！')


# 请求网页获取源代码
def get_html(base_url, headers):
    """
    功能：请求base_url地址获得源代码
    参数：base_url headers
    返回值：HTML源码
    """
    try:
        response = requests.get(url=base_url, headers=headers)  # 请求视频主页获取源代码html
        response.raise_for_status()  # 主动抛出异常
        response.encoding = response.apparent_encoding   # 自动去检查html用的是什么编码
        return response.text  # 返回编码后的html文本文件
    except:
        return None


# 解析源码获得m3u8_url
def parse_html(html):
    """
    功能：解析源代码的HTML获得json数据，再从中解析出m3u8_url
    参数：html
    返回值：m3u8_url
    """
    # 你想要的地方就写上(.*?)
    result = re.findall(r'<script type="text/javascript">var player_aaaa=(.*?)</script>', html, re.S)[0]
    # 将json_str ==> json_dict
    json_dict = json.loads(result)
    vod_name = json_dict['vod_data']['vod_name']  # 标题
    m3u8_url = json_dict['url']
    dic = {
        'vod_name':  vod_name,
        'm3u8_url': m3u8_url
    }
    return dic


# 获取m3u8文件
def get_m3u8(m3u8_result, headers):
    """
    功能：根据m3u8_url获得m3u8文件
    参数：m3u8_result
    返回值：m3u8文件
    """
    # 创建一级文件夹
    if not os.path.exists('./白狐视频'):
        os.mkdir('./白狐视频')

    # 创建二级文件夹
    if not os.path.exists(f'./白狐视频/{m3u8_result["vod_name"]}'):
        os.mkdir(f'./白狐视频/{m3u8_result["vod_name"]}')

    try:
        response = requests.get(url=m3u8_result['m3u8_url'], headers=headers)

        # 利用换行的特点，将字符串分成三份。
        string = response.text.split('\n')[-1]
        # 构造mixed_m3u8_url
        mixed_m3u8_url = re.sub(r'index.m3u8', string, m3u8_result['m3u8_url'])
        # 发送请求获取m3u8文件
        resp = requests.get(url=mixed_m3u8_url, headers=headers)
        # 将文件保存在本地
        with open(f'./白狐视频/{m3u8_result["vod_name"]}/mixed.m3u8', 'w') as f:
            f.write(resp.text)

        return mixed_m3u8_url

    except BaseException as e:
        print(e)
        return None


# 主函数
def main():
    base_url = 'https://www.bhxoxo.com/p/152566-1-1.html'
    # 爬虫是模拟人的请求（伪装）
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.35',
    }
    # 请求视频主页获取源代码
    html = get_html(base_url, headers)
    if html:
        print('获取视频主页成功！')
        result = parse_html(html)
        if result:
            print('获取m3u8_url成功！')
            mixed_m3u8_url = get_m3u8(result, headers)
            get_ts(result, headers, mixed_m3u8_url)

        else:
            print('获取m3u8_url失败！')
    else:
        print('获取视频主页失败')
    

if __name__ == '__main__':
    main()
